package com.learning.netease.readingwritingseparation.datasource;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ClassName: DynamicPl
 * Description:
 * Date: 2019/3/29 16:12 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class DynamicDataSourceInterceptor implements Interceptor {

    private static final Map<String, DynamicDataSourceGlobel> CACHE = new ConcurrentHashMap<>();
    private static final String REGEX = ".*insert$|.*delete$|.update$";


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 如果拥有事务上下文，则将连接绑定到事务上下文中
        boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();

        DynamicDataSourceGlobel dynamicDataSourceGlobel;
        if (!synchronizationActive) {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            dynamicDataSourceGlobel = CACHE.get(ms.getId());

            if (Objects.isNull(dynamicDataSourceGlobel)) {
                // 读方法
                if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {

                    // "!selectKey" 为自增id查询主键（SELECT LAST_INSERT_ID()）方法
                    if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
                        dynamicDataSourceGlobel = DynamicDataSourceGlobel.WRITE;
                    } else {
                        BoundSql boundSql = ms.getSqlSource().getBoundSql(args[1]);
                        String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");
                        if (sql.matches(REGEX)) {
                            dynamicDataSourceGlobel = DynamicDataSourceGlobel.WRITE;
                        } else {
                            dynamicDataSourceGlobel = DynamicDataSourceGlobel.READ;
                        }
                    }
                } else {
                    // 写方法
                    dynamicDataSourceGlobel = DynamicDataSourceGlobel.WRITE;
                }

                log.info("设置方法[{}] use [{}] strategy,SqlCommandType {}", ms.getId(), dynamicDataSourceGlobel.name(), ms.getSqlCommandType().name());
                CACHE.put(ms.getId(), dynamicDataSourceGlobel);

            }

            DynamicDataSourceHolder.putDataSource(dynamicDataSourceGlobel);
        }


        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
